/*
 * @(#)SwiftApiInvocation.java   1.0  2022年1月18日
 * 
 * Copyright (c)	2014-2020. All Rights Reserved.	GuangZhou hhmk Technology Company LTD.
 */
package com.swift.core.api.invocation;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swift.core.api.ApiEngine.CallBackApiEngine;
import com.swift.core.api.ApiEngine.ResponseEngine;
import com.swift.core.api.OpenApi;
import com.swift.core.api.Sign;
import com.swift.core.api.asyn.ChangeFuture;
import com.swift.core.api.rpc.ClientEngine;
import com.swift.core.api.rpc.ClientEngine.CallBackEngine;
import com.swift.core.auth.AuthProtocol;
import com.swift.core.model.ServiceRequest;
import com.swift.core.model.ServiceResponse;
import com.swift.core.model.data.AbstractBeanDataModel;
import com.swift.core.model.data.DataModel;
import com.swift.core.model.data.MapDataModel;
import com.swift.core.session.SessionAuth;
import com.swift.core.spring.Spring;
import com.swift.core.spring.proxy.AbstractSwiftInvocation;
import com.swift.exception.ResultCode;
import com.swift.exception.ServiceException;
import com.swift.exception.extend.SystemException;
import com.swift.util.bean.AnnotationUtil;
import com.swift.util.bean.BeanUtil;
import com.swift.util.math.RandomUtil;
import com.swift.util.security.MD5Utils;
import com.swift.util.type.JsonUtil;
import com.swift.util.type.TypeUtil;

/**
 * 添加说明
 * 
 * @author zhengjiajin
 * @version 1.0 2022年1月18日
 */
public class SwiftApiInvocation extends AbstractSwiftInvocation {

    private static final Logger log = LoggerFactory.getLogger(SwiftApiInvocation.class);

    private String sysId;
    
    @SuppressWarnings("unchecked")
    public ClientEngine<ServiceRequest, ServiceResponse> getEngineClient(OpenApi openApi) {
        ClientEngine<ServiceRequest, ServiceResponse> clientEngine = Spring.getBean(openApi.clientEngineName(), ClientEngine.class);
        return clientEngine;
    }
    
    public SessionAuth getSessionAuth() {
        try {
            SessionAuth sessionAuth = Spring.getBean(SessionAuth.class);
            return sessionAuth;
        }catch(Exception ex) {
            return null;
        }
    }
    
    public synchronized String getSysId() {
        if(sysId==null) sysId=Spring.getProperties().getProperty("sysId");
        return sysId;
    }

    /**
     * @see com.swift.core.spring.proxy.AbstractSwiftInvocation#invokeInterface(java.lang.Object,
     *      java.lang.reflect.Method, java.lang.Object[])
     */
    @Override
    public Object invokeInterface(Object proxy, Method method, Object[] args) throws Throwable {
      //得到注解
        Class<?> cla = getInvokeClass(args);
        OpenApi openApi = AnnotationUtil.getAnnotation(cla, OpenApi.class);
        if(openApi==null) return null;
        String methodName = method.getName();
        ServiceRequest req = new ServiceRequest();
        if(TypeUtil.isNotNull(openApi.value())) {
            req.setDomain(openApi.value());
            if(openApi.value().indexOf(".")!=-1) {
                req.setSysId(openApi.value().substring(0,openApi.value().indexOf(".")));
            }else {
                req.setSysId(openApi.value());
            }
        }
        req.setMethod(openApi.method());
        req.setInterfaceVersion(openApi.version());
        req.setReqId(RandomUtil.createReqId());
        req.setRequestTime(System.currentTimeMillis());
        req.setData(getReqData(openApi, args));
        addAuthRequest(req, openApi);
        addSessionRequest(req, openApi);
        if(methodName.equals("sendRequestNoReturn")) {
            getEngineClient(openApi).sendRequestNoReturn(req);
            return null;
        }else if(methodName.equals("sendRequest")) {
            //3个参数的为异步调用
            if(args.length==3) {
                CallBackApiEngine callBack = (CallBackApiEngine)args[2];
                getEngineClient(openApi).sendRequest(req, new CallBackEngine<ServiceResponse>() {
                    @Override
                    public void receiveResponse(ServiceResponse rsp) {
                        ResponseEngine res = new ResponseEngine();
                        res.setResultCode(rsp.getResultCode());
                        res.setReason(rsp.getReason());
                        res.setRequest(args[1]);
                        res.setValue(getResponse(cla,openApi, rsp.getData()));
                        callBack.receiveResponse(res);
                    }

                    @Override
                    public void failed(Throwable ex) {
                        log.error("系统异常",ex);
                        ResponseEngine res = new ResponseEngine();
                        if(ex instanceof ServiceException) {
                            ServiceException serviceException = (ServiceException)ex;
                            res.setResultCode(serviceException.getStatusCode());
                            res.setReason(serviceException.getMessage());
                        }else {
                            res.setResultCode(ResultCode.SYS_ERROR);
                            res.setReason("系统异常");
                        }
                        res.setRequest(args[1]);
                        callBack.receiveResponse(res);
                    }
                });
            } else {
                ServiceResponse rsp = getEngineClient(openApi).sendRequest(req);
                if(rsp==null) throw new SystemException("网络异常,返回结果为空");
                if(rsp.getResultCode()!=0)  throw new ServiceException(rsp.getResultCode(),rsp.getReason());
                return getResponse(cla,openApi, rsp.getData());
            }
        } else if(methodName.equals("sendAsynRequest")) {
            Future<ServiceResponse> rsp = getEngineClient(openApi).sendAsynRequest(req);
            ChangeFuture<ServiceResponse,Object> changeFuture = new  ChangeFuture<>(rsp,(t)->{
                if(t==null) throw new SystemException("网络异常,返回结果为空");
                if(t.getResultCode()!=0)  throw new ServiceException(t.getResultCode(),t.getReason());
                return getResponse(cla,openApi, t.getData());
            });
            return changeFuture;
        }
        throw new SystemException("系统间调用接口禁止使用自定义方法");
    }

    private Object getResponse(Class<?> cla,OpenApi openApi,DataModel resData) {
        Type type = getResponseType(cla);
        if(type==null) return null;
        if(type.getTypeName().equals(Void.class.getTypeName())) return null;
        if(TypeUtil.isNull(openApi.resParam())) {
            return valueToType(resData, type);
        }
        return valueToType(resData.getObject(openApi.resParam()), type);
    }
    
    private void addAuthRequest(ServiceRequest req,OpenApi openApi) {
        String sysId = getSysId();
        if(TypeUtil.isNull(sysId)) return;
        if(TypeUtil.isNull(req.getSysId())) return;
        DataModel data = req.getData();
        if(data instanceof AbstractBeanDataModel) {
            data = MapDataModel.beanToMap((AbstractBeanDataModel)data);
        }
        //先加密
        if(TypeUtil.isNotNull(openApi.encryption())) {
            //TODO
        }
        //后签名
        if(TypeUtil.isNotNull(openApi.sign()) && openApi.sign().length>0) {
            Sign sign = openApi.sign()[0];
            byte[] key = BeanUtil.newInstance(sign.getKey()).getKey(openApi.value(), sign.value());
            if(key==null || key.length<=0) return;
            String key_str = new String(key);
            // 将dataModel转为json //TODO暂时先固定签名方式
            String value = JsonUtil.toJson(DataModel.collection(data));
            // 开始签名:  MD5(系统ID+请求JSON参数+APPKEY)
            log.info("签名字符串:"+sysId + value + key_str);
            String mySign = MD5Utils.getMD5Code(sysId + value + key_str);
            log.info("签名字结果:"+mySign);
            data.addObject(AuthProtocol.PROTOCOL_SING, mySign);
        }
        data.addObject(AuthProtocol.PROTOCOL_SYSID, sysId);
        req.setData(data);
    }
    
    private void addSessionRequest(ServiceRequest req,OpenApi openApi) {
        SessionAuth sessionAuth = getSessionAuth();
        if(sessionAuth==null) return;
        if(TypeUtil.isNull(openApi.sessionUser())) return;
        if(TypeUtil.isNull(req.getData())) return;
        String sessionUser = req.getData().getString(openApi.sessionUser());
        if(TypeUtil.isNull(sessionUser)) return;
        req.setSessionUser(sessionAuth.decrypt(sessionUser));
    }
    
    private DataModel getReqData(OpenApi openApi,Object[] args) {
        if(args==null) return null;
        if(args.length<2) return null;
        Object reqObj = args[1];
        if(reqObj==null) return null;
        if(reqObj instanceof Void) return null; 
        if(TypeUtil.isNull(openApi.reqParam())) {
            if(!(reqObj instanceof DataModel)) throw new SystemException("非DataModel类型输入请定义输入参数");
            return (DataModel)reqObj;
        } else {
            DataModel reqDataModel = new MapDataModel();
            reqDataModel.putObject(openApi.reqParam(), reqObj);
            return reqDataModel;
        }
    }
}
